# Copyright (c) 2019 Presto Labs Pte. Ltd.
# Author: jaewon

import datetime
from collections import namedtuple

from coin.base.datetime_util import to_datetime
from coin.exchange.kr_rest.product_holders import get_holder_from_product

# Spot
from coin.exchange.binance.kr_rest.product import BinanceProduct
from coin.exchange.bitstamp_v2.kr_rest.product import BitstampProduct
from coin.exchange.gdax.kr_rest.product import GdaxProduct
from coin.exchange.huobi.kr_rest.product import HuobiProduct
from coin.exchange.kraken.kr_rest.product import KrakenProduct
from coin.exchange.okex.kr_rest.product import OkexProduct

# Kr Spot
from coin.exchange.bithumb.kr_rest.product import BithumbProduct
from coin.exchange.upbit_v1.kr_rest.product import UpbitProduct

# Derivatives
from coin.exchange.bitflyer_v1.kr_rest.futures_product import BitflyerFuturesProduct
from coin.exchange.bitmex.kr_rest.futures_product import BitmexFuturesProduct
from coin.exchange.huobi_futures.kr_rest.futures_product import HuobiFuturesProduct
from coin.exchange.kraken_futures.kr_rest.futures_product import KrakenFuturesProduct
from coin.exchange.okex_futures.kr_rest.futures_product import OkexFuturesProduct

_SymbolInfo = namedtuple('SymbolInfo',
                         [
                             'nickname',
                             'product',
                             'product_info',
                             'sub_req',
                             'inverse',
                             'price_multiplier',
                             'maker_fee',
                             'taker_fee'
                         ])


def symbol_info(nickname,
                product,
                sub_req,
                inverse=False,
                price_multiplier=1.,
                maker_fee_bp=0.,
                taker_fee_bp=0.):
  return _SymbolInfo(nickname,
                     product,
                     get_holder_from_product(product).product_info,
                     sub_req,
                     inverse,
                     price_multiplier,
                     maker_fee=maker_fee_bp / 10000.,
                     taker_fee=taker_fee_bp / 10000.)


def universe_all(timestamp=None):
  return (universe_btc(timestamp) + universe_eth(timestamp))


def universe_btc(timestamp=None):
  timestamp = to_datetime(timestamp or datetime.datetime.now())

  symbols = [
      # Futures
      symbol_info('bitflyer-fxbtcjpy',
                  BitflyerFuturesProduct.FromStr('BTC-JPY.PERPETUAL'),
                  'Futures.Bitflyer',
                  maker_fee_bp=0.,
                  taker_fee_bp=0.,
                  price_multiplier=1. / 108),

      # Futures, inverse
      symbol_info('bitmex-xbtusd',
                  BitmexFuturesProduct.FromStr('BTC-USD.PERPETUAL'),
                  'Futures.Bitmex',
                  maker_fee_bp=-2.5,
                  taker_fee_bp=7.5,
                  inverse=True),
      symbol_info('huobi-btcusd-quarter',
                  HuobiFuturesProduct.FromStr('BTC-USD.QUARTER', timestamp),
                  'Futures.Huobi',
                  maker_fee_bp=-1.3,
                  taker_fee_bp=2.,
                  inverse=True),
      symbol_info('huobi-btcusd-next_week',
                  HuobiFuturesProduct.FromStr('BTC-USD.NEXT_WEEK', timestamp),
                  'Futures.Huobi',
                  maker_fee_bp=-1.3,
                  taker_fee_bp=2.,
                  inverse=True),
      symbol_info('huobi-btcusd-this_week',
                  HuobiFuturesProduct.FromStr('BTC-USD.THIS_WEEK', timestamp),
                  'Futures.Huobi',
                  maker_fee_bp=-1.3,
                  taker_fee_bp=2.,
                  inverse=True),
      symbol_info('kraken-btcusd-perpetual',
                  KrakenFuturesProduct.FromStr('BTC-USD.PERPETUAL'),
                  'Futures.Kraken',
                  maker_fee_bp=-2.5,
                  taker_fee_bp=7.5,
                  inverse=True),
      symbol_info('okex-btcusd-quarter',
                  OkexFuturesProduct.FromStr('BTC-USD.QUARTER', timestamp),
                  'Futures.Okex.v3',
                  maker_fee_bp=-1.,
                  taker_fee_bp=2.,
                  inverse=True),
      symbol_info('okex-btcusd-next_week',
                  OkexFuturesProduct.FromStr('BTC-USD.NEXT_WEEK', timestamp),
                  'Futures.Okex.v3',
                  maker_fee_bp=-1.,
                  taker_fee_bp=2.,
                  inverse=True),
      symbol_info('okex-btcusd-this_week',
                  OkexFuturesProduct.FromStr('BTC-USD.THIS_WEEK', timestamp),
                  'Futures.Okex.v3',
                  maker_fee_bp=-1.,
                  taker_fee_bp=2.,
                  inverse=True),
      symbol_info('okex-btcusd-perpetual',
                  OkexFuturesProduct.FromStr('BTC-USD.PERPETUAL'),
                  'Futures.Okex.v3_swap',
                  maker_fee_bp=-3.,
                  taker_fee_bp=4.8,
                  inverse=True),

      # Spot, USD quote
      symbol_info('binance-btcusdt', BinanceProduct.FromStr('BTC-USDT'), 'Spot.Binance.snapshot'),
      symbol_info('bitstamp-btcusd', BitstampProduct.FromStr('BTC-USD'), 'Spot.Bitstamp'),
      symbol_info('gdax-btcusd', GdaxProduct.FromStr('BTC-USD'), 'Spot.Gdax'),
      symbol_info('huobi-btcusdt', HuobiProduct.FromStr('BTC-USDT'), 'Spot.Huobi'),
      symbol_info('kraken-btcusd', KrakenProduct.FromStr('BTC-USD'), 'Spot.Kraken'),
      symbol_info('okex_btcusdt', OkexProduct.FromStr('BTC-USDT'), 'Spot.Okex.v3'),

      # Spot, other quote
      symbol_info('bitflyer-btcjpy',
                  BitflyerFuturesProduct.FromStr('BTC-JPY.IMMEDIATE'),
                  'Futures.Bitflyer',
                  price_multiplier=1. / 108),
      symbol_info('bithumb-btckrw',
                  BithumbProduct.FromStr('BTC-KRW'),
                  'Spot.Bithumb',
                  price_multiplier=1. / 1180),
      symbol_info('upbit-btckrw',
                  UpbitProduct.FromStr('BTC-KRW'),
                  'Spot.Upbit',
                  price_multiplier=1. / 1180),
  ]

  return symbols


def universe_eth(timestamp=None):
  timestamp = to_datetime(timestamp or datetime.datetime.now())

  symbols = [
      # Futures, inverse
      symbol_info('bitmex-ethusd',
                  BitmexFuturesProduct.FromStr('ETH-USD.PERPETUAL'),
                  'Futures.Bitmex',
                  maker_fee_bp=-2.5,
                  taker_fee_bp=7.5,
                  inverse=True),
      symbol_info('huobi-ethusd-quarter',
                  HuobiFuturesProduct.FromStr('ETH-USD.QUARTER', timestamp),
                  'Futures.Huobi',
                  maker_fee_bp=-1.3,
                  taker_fee_bp=2.,
                  inverse=True),
      symbol_info('huobi-ethusd-next_week',
                  HuobiFuturesProduct.FromStr('ETH-USD.NEXT_WEEK', timestamp),
                  'Futures.Huobi',
                  maker_fee_bp=-1.3,
                  taker_fee_bp=2.,
                  inverse=True),
      symbol_info('huobi-ethusd-this_week',
                  HuobiFuturesProduct.FromStr('ETH-USD.THIS_WEEK', timestamp),
                  'Futures.Huobi',
                  maker_fee_bp=-1.3,
                  taker_fee_bp=2.,
                  inverse=True),
      symbol_info('kraken-ethusd-perpetual',
                  KrakenFuturesProduct.FromStr('ETH-USD.PERPETUAL'),
                  'Futures.Kraken',
                  maker_fee_bp=-2.5,
                  taker_fee_bp=7.5,
                  inverse=True),
      symbol_info('okex-ethusd-quarter',
                  OkexFuturesProduct.FromStr('ETH-USD.QUARTER', timestamp),
                  'Futures.Okex.v3',
                  maker_fee_bp=-1.,
                  taker_fee_bp=2.,
                  inverse=True),
      symbol_info('okex-ethusd-next_week',
                  OkexFuturesProduct.FromStr('ETH-USD.NEXT_WEEK', timestamp),
                  'Futures.Okex.v3',
                  maker_fee_bp=-1.,
                  taker_fee_bp=2.,
                  inverse=True),
      symbol_info('okex-ethusd-this_week',
                  OkexFuturesProduct.FromStr('ETH-USD.THIS_WEEK', timestamp),
                  'Futures.Okex.v3',
                  maker_fee_bp=-1.,
                  taker_fee_bp=2.,
                  inverse=True),
      symbol_info('okex-ethusd-perpetual',
                  OkexFuturesProduct.FromStr('ETH-USD.PERPETUAL'),
                  'Futures.Okex.v3_swap',
                  maker_fee_bp=-3.,
                  taker_fee_bp=4.8,
                  inverse=True),

      # Spot, USD quote
      symbol_info('binance-ethusdt', BinanceProduct.FromStr('ETH-USDT'), 'Spot.Binance.snapshot'),
      symbol_info('bitstamp-ethusd', BitstampProduct.FromStr('ETH-USD'), 'Spot.Bitstamp'),
      symbol_info('gdax-ethusd', GdaxProduct.FromStr('ETH-USD'), 'Spot.Gdax'),
      symbol_info('huobi-ethusdt', HuobiProduct.FromStr('ETH-USDT'), 'Spot.Huobi'),
      symbol_info('kraken-ethusd', KrakenProduct.FromStr('ETH-USD'), 'Spot.Kraken'),
      symbol_info('okex-ethusdt', OkexProduct.FromStr('ETH-USDT'), 'Spot.Okex.v3'),

      # Spot, other quote
      symbol_info('bithumb-ethkrw',
                  BithumbProduct.FromStr('ETH-KRW'),
                  'Spot.Bithumb',
                  price_multiplier=1. / 1180),
      symbol_info('upbit-ethkrw',
                  UpbitProduct.FromStr('ETH-KRW'),
                  'Spot.Upbit',
                  price_multiplier=1. / 1180),
  ]

  return symbols
